package com.w.picture.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.w.picture.exception.BusinessException;
import com.w.picture.exception.ErrorCode;
import com.w.picture.exception.ThrowUtils;
import com.w.picture.mapper.SpaceMapper;
import com.w.picture.mode.dto.space.analyze.*;
import com.w.picture.mode.entity.Picture;
import com.w.picture.mode.entity.Space;
import com.w.picture.mode.entity.User;
import com.w.picture.mode.vo.space.analyze.*;
import com.w.picture.service.PictureService;
import com.w.picture.service.SpaceAnalyzeService;
import com.w.picture.service.SpaceService;
import com.w.picture.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wzp
 * @since 2025/1/26
 */
@Slf4j
@Service
public class SpaceAnalyzeServiceImpl extends ServiceImpl<SpaceMapper, Space> implements SpaceAnalyzeService {

    @Resource
    private UserService userService;
    @Resource
    private SpaceService spaceService;
    @Resource
    private PictureService pictureService;


    /**
     * 获取空间使用分析数据
     * 注意:
     * 如果是分析全空间或公共图库的使用情况,需要编写:仅管理员可访问的校验逻辑,并且更改查询图片表的范围;
     * 如果只是分析单个空间的使用情况,直接从空间表查询出单个空间的数据即可.
     *
     * @param request   请求参数
     * @param loginUser 登录用户
     * @return 响应数据
     */
    @Override
    public SpaceUsageAnalyzeResponse getSpaceUsageAnalyze(SpaceAnalyzeRequest request, User loginUser) {
        // 检查参数
        ThrowUtils.throwIf(request == null, ErrorCode.PARAMS_ERROR, "请求参数为空");
        Long spaceId = request.getSpaceId();
        boolean isQueryPublic = request.isQueryPublic();
        boolean isQueryAll = request.isQueryAll();
        // 判断 查询全部的使用情况 或者 公共图库的使用情况 还是 私有空间的使用情况
        if (isQueryAll || isQueryPublic) {
            // 判断 登录的用户是否为管理员
            ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR);
            // 开始查询数据库结果
            QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("picSize"); // 查询出图片的大小
            fillAnalyzeQueryWrapper(request, queryWrapper); // 填充查询对象,判断是否要添加空间id
            // 设置查询条件为 拿到所有的 picSize 图片大小，但只返回每个记录的第一个字段的值,因为也只查询了这一列 picSize
            // selectObjs(queryWrapper) 只返回每个记录的第一个字段的值
            // 之所以使用这个方法,并且只获取需要的字段,主要就是为了提高性能并节约存储空间.
            // 查全部sql = SELECT picSize FROM picture WHERE isDelete=0
            // 查公共图库sql = SELECT picSize FROM picture WHERE isDelete=0 AND (spaceId IS NULL)
            // 查私有空间sql = SELECT picSize FROM picture WHERE isDelete=0 AND spaceId=1
            List<Object> pictureList = pictureService.getBaseMapper().selectObjs(queryWrapper);
            // 计算总大小
            long totalSize = Optional.ofNullable(pictureList)
                    .orElse(Collections.emptyList())
                    .stream()
                    .mapToLong(obj -> Long.parseLong(obj.toString()))
                    .sum();
            int size = Optional.ofNullable(pictureList).orElse(Collections.emptyList()).size();
            // 封装结果返回
            SpaceUsageAnalyzeResponse response = new SpaceUsageAnalyzeResponse();
            response.setUsedSize(totalSize);
            response.setUsedCount((long) size);
            // 公共图库无上限,无比例
            response.setMaxSize(null);
            response.setSizeUsageRatio(null);
            response.setMaxCount(null);
            response.setCountUsageRatio(null);
            return response;
        } else {
            // 私有空间
            ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR);
            // 获取空间信息
            Space space = spaceService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");

            // 权限校验:仅空间所有者和管理员可以访问
            spaceService.checkSpaceAuth(space, loginUser);

            // 封装结果返回

            // 空间使用比例
            double sizeUsageRatio = NumberUtil.round(space.getTotalSize() * 100 / space.getMaxSize(), 2).doubleValue();
            // 图片数量占比
            double countUsageRatio = NumberUtil.round(space.getTotalCount() * 100 / space.getMaxCount(), 2).doubleValue();

            SpaceUsageAnalyzeResponse response = new SpaceUsageAnalyzeResponse();
            response.setUsedSize(space.getTotalSize());
            response.setUsedCount(space.getTotalCount());
            response.setMaxSize(space.getMaxSize());
            response.setMaxCount(space.getMaxCount());
            response.setSizeUsageRatio(sizeUsageRatio);
            response.setCountUsageRatio(countUsageRatio);
            return response;
        }
    }

    /**
     * 获取空间分类分析数据
     *
     * @param request
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceCategoryAnalyzeResponse> getSpaceCategoryAnalyze(SpaceCategoryAnalyzeRequest request, User loginUser) {
        ThrowUtils.throwIf(request == null, ErrorCode.PARAMS_ERROR);

        // 检查权限
        checkSpaceAnalyzeAuth(request, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        // 根据分析范围补充查询条件
        fillAnalyzeQueryWrapper(request, queryWrapper);

        // 查询出每个分类下的图片数量和图片总大小
        // 查全部sql = SELECT category AS category,COUNT(*) AS count,SUM(picSize) AS totalSize FROM picture WHERE isDelete=0 GROUP BY category
        // 查公共图库sql = SELECT category AS category,COUNT(*) AS count,SUM(picSize) AS totalSize FROM picture WHERE isDelete=0 AND (spaceId IS NULL) GROUP BY category
        // 查私有空间sql = SELECT category AS category,COUNT(*) AS count,SUM(picSize) AS totalSize FROM picture WHERE isDelete=0 AND (spaceId = '1880816765936607234') GROUP BY category
        queryWrapper.select("category AS category",
                "COUNT(*) AS count",
                "SUM(picSize) AS totalSize").groupBy("category");

        // 查询并转换结果
        List<Map<String, Object>> maps = pictureService.getBaseMapper().selectMaps(queryWrapper);
        return maps.stream().map(result -> {
            String category = result.get("category") != null ? result.get("category").toString() : "未分类";
            Long count = ((Number) result.get("count")).longValue();
            Long totalSize = ((Number) result.get("totalSize")).longValue();
            return new SpaceCategoryAnalyzeResponse(category, count, totalSize);
        }).collect(Collectors.toList());
    }

    /**
     * 获取空间标签分析数据
     *
     * @param request
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceTagAnalyzeResponse> getSpaceTagAnalyze(SpaceTagAnalyzeRequest request, User loginUser) {
        ThrowUtils.throwIf(request == null, ErrorCode.PARAMS_ERROR);

        // 检查权限
        checkSpaceAnalyzeAuth(request, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        // 根据分析范围补充查询条件
        fillAnalyzeQueryWrapper(request, queryWrapper);

        // 查询所有符合条件的标签
        queryWrapper.select("tags");
        // 查全部sql = SELECT tags FROM picture WHERE isDelete=0
        // 查公共图库sql = SELECT tags FROM picture WHERE isDelete=0 AND (spaceId IS NULL)
        // 查私有空间sql = SELECT tags FROM picture WHERE isDelete=0 AND spaceId=1
        List<Object> objects = pictureService.getBaseMapper().selectObjs(queryWrapper);
        // 合并所有标签并统计使用次数
        Map<String, Long> tagCountMap = Optional.ofNullable(objects)
                .orElse(Collections.emptyList())
                .stream()
                // 过滤掉null对象
                .filter(ObjectUtil::isNotNull)
                // 将每个对象转换为字符串
                .map(Object::toString)
                // 转换每个tags ["搞笑"] 为Stream<String>
                .flatMap(tags -> JSONUtil.toList(tags, String.class).stream())
                // 合并所有标签并统计使用次数
                .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()));

        // 转换为响应对象，按使用次数降序排序
        return tagCountMap.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue())) // 降序排列
                .map(entry -> new SpaceTagAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 获取空间大小分析数据
     *
     * @param spaceSizeAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceSizeAnalyzeResponse> getSpaceSizeAnalyze(SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceSizeAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);

        // 检查权限
        checkSpaceAnalyzeAuth(spaceSizeAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceSizeAnalyzeRequest, queryWrapper);

        // 查询所有符合条件的图片大小
        queryWrapper.select("picSize");
        // 查全部sql = SELECT picSize FROM picture WHERE isDelete=0
        // 查公共图库sql = SELECT picSize FROM picture WHERE isDelete=0 AND (spaceId IS NULL)
        // 查私有空间sql = SELECT picSize FROM picture WHERE isDelete=0 AND (spaceId = '1880816765936607234')
        List<Long> picSizes = pictureService.getBaseMapper().selectObjs(queryWrapper)
                .stream()
                .map(size -> ((Number) size).longValue())
                .collect(Collectors.toList());

        // 定义分段范围，注意使用有序 Map
        Map<String, Long> sizeRanges = new LinkedHashMap<>();
        sizeRanges.put("<100KB", picSizes.stream().filter(size -> size < 100 * 1024).count());
        sizeRanges.put("100KB-500KB", picSizes.stream().filter(size -> size >= 100 * 1024 && size < 500 * 1024).count());
        sizeRanges.put("500KB-1MB", picSizes.stream().filter(size -> size >= 500 * 1024 && size < 1 * 1024 * 1024).count());
        sizeRanges.put(">1MB", picSizes.stream().filter(size -> size >= 1 * 1024 * 1024).count());

        // 转换为响应对象
        return sizeRanges.entrySet().stream()
                .map(entry -> new SpaceSizeAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 获取空间用户分析数据(根据时间段,或者 用户id)
     *
     * @param spaceUserAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceUserAnalyzeResponse> getSpaceUserAnalyze(SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceUserAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        // 检查权限
        checkSpaceAnalyzeAuth(spaceUserAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        Long userId = spaceUserAnalyzeRequest.getUserId();
        queryWrapper.eq(ObjUtil.isNotNull(userId), "userId", userId);
        fillAnalyzeQueryWrapper(spaceUserAnalyzeRequest, queryWrapper);

        // 分析维度：每日、每周、每月
        String timeDimension = spaceUserAnalyzeRequest.getTimeDimension();
        switch (timeDimension) {
            case "day":
                // 日期格式化到日期
                queryWrapper.select("DATE_FORMAT(createTime, '%Y-%m-%d') AS period", "COUNT(*) AS count");
                break;
            case "week":
                // 通过年份周数计算
                queryWrapper.select("YEARWEEK(createTime) AS period", "COUNT(*) AS count");
                break;
            case "month":
                // 日期格式化到月份
                queryWrapper.select("DATE_FORMAT(createTime, '%Y-%m') AS period", "COUNT(*) AS count");
                break;
            default:
                throw new BusinessException(ErrorCode.PARAMS_ERROR.getCode(), "不支持的时间维度");
        }

        // 分组和排序
        queryWrapper.groupBy("period").orderByAsc("period");

        // 查询结果并转换
        // 注意:timeDimension参数不同,拿到的 period 时段数据不同
        // 查全部sql = SELECT DATE_FORMAT(createTime, '%Y-%m-%d') AS period,COUNT(*) AS count FROM picture WHERE isDelete=0  GROUP BY period ORDER BY period ASC
        // 查公共图库sql = SELECT DATE_FORMAT(createTime, '%Y-%m-%d') AS period,COUNT(*) AS count FROM picture WHERE isDelete=0 AND (spaceId IS NULL) GROUP BY period ORDER BY period ASC
        // 查私有空间sql = SELECT DATE_FORMAT(createTime, '%Y-%m-%d') AS period,COUNT(*) AS count FROM picture WHERE isDelete=0 AND (userId = ? AND spaceId = ?) GROUP BY period ORDER BY period ASC
        List<Map<String, Object>> queryResult = pictureService.getBaseMapper().selectMaps(queryWrapper);
        return queryResult.stream()
                .map(result -> {
                    String period = result.get("period").toString();
                    Long count = ((Number) result.get("count")).longValue();
                    return new SpaceUserAnalyzeResponse(period, count);
                })
                .collect(Collectors.toList());
    }

    /**
     * 获取空间排行分析数据-根据空间的使用情况排序
     *
     * @param spaceRankAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<Space> getSpaceRankAnalyze(SpaceRankAnalyzeRequest spaceRankAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceRankAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);

        // 仅管理员可查看空间排行
        ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR, "无权查看空间排行");

        // 构造查询条件
        // SQL = SELECT id,spaceName,userId,totalSize FROM space WHERE isDelete=0 ORDER BY totalSize DESC LIMIT 10
        QueryWrapper<Space> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "spaceName", "userId", "totalSize")
                .orderByDesc("totalSize")
                .last("LIMIT " + spaceRankAnalyzeRequest.getTopN()); // 取前 N 名

        // 查询结果
        return spaceService.list(queryWrapper);
    }

    /**
     * 公共的检查空间分析权限
     *
     * @param request
     * @param loginUser
     */
    private void checkSpaceAnalyzeAuth(SpaceAnalyzeRequest request, User loginUser) {
        // 检查权限
        if (request.isQueryAll() || request.isQueryPublic()) {
            // 全空间分析 或者 公共图库权限校验: 仅管理员可以访问
            ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR);
        } else {
            // 私有空间校验
            Long spaceId = request.getSpaceId();
            ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR);
            Space space = spaceService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
            spaceService.checkSpaceAuth(space, loginUser);
        }
    }

    /**
     * 根据分析范围填充查询对象
     *
     * @param request
     * @param queryWrapper
     */
    private static void fillAnalyzeQueryWrapper(SpaceAnalyzeRequest request, QueryWrapper<Picture> queryWrapper) {
        if (request.isQueryAll()) {
            return;
        }
        if (request.isQueryPublic()) {
            queryWrapper.isNull("spaceId");
            return;
        }
        Long spaceId = request.getSpaceId();
        if (spaceId != null) {
            queryWrapper.eq("spaceId", spaceId);
            return;
        }
        throw new BusinessException(ErrorCode.PARAMS_ERROR.getCode(), "未指定查询范围");
    }
}
